#include <QtWidgets>
#include "spinparser.h"

#define KEY_ELEMENT_SEP ":"

SpinParser::SpinParser()
{
    setKind(&SpinKinds[SpinParser::K_NONE],     false,'n', "none", "none"); // place-holder only
    setKind(&SpinKinds[SpinParser::K_CONST],    true, 'c', "constant", "constants");
    setKind(&SpinKinds[SpinParser::K_PUB],      true, 'f', "public", "methods");
    setKind(&SpinKinds[SpinParser::K_PRI],      true, 'p', "private", "functions");
    setKind(&SpinKinds[SpinParser::K_OBJECT],   true, 'o', "obj", "objects");
    setKind(&SpinKinds[SpinParser::K_VAR],      true, 'v', "var", "variables");
    setKind(&SpinKinds[SpinParser::K_DAT],      true, 'x', "dat", "dat");

    KeyWord keyCon = {"con", K_CONST, 0};
    KeyWord keyObj = {"obj", K_OBJECT, 0};
    KeyWord keyPub = {"pub", K_PUB, 0};
    KeyWord keyPri = {"pri", K_PRI, 0};
    KeyWord keyVar = {"var", K_VAR, 0};
    KeyWord keyDat = {"dat", K_DAT, 0};
    KeyWord keyNull= {NULL,  K_NONE, 0};

    spin_keywords.append(keyCon);
    spin_keywords.append(keyObj); // keep as item 1.
    spin_keywords.append(keyPub);
    spin_keywords.append(keyPri);
    spin_keywords.append(keyVar);
    spin_keywords.append(keyDat);
    spin_keywords.append(keyNull);
}

SpinParser::~SpinParser()
{
    clearDB();
}

void SpinParser::clearDB()
{
    db.clear();
    spinFiles.clear();
}

void SpinParser::setKind(kindOption *kind, bool en, const char letter, const char *name, const char *desc)
{
    kind->enabled = en;
    kind->letter = letter;
    kind->name = name;
    kind->description = desc;
}

/*
 * get a tree list.
 * all object instances will be listed with sub-objects
 * the list will be indented according to relative position
 */
QStringList SpinParser::spinFileTree(QString file, QString libpath)
{
    QString key;
    QString value;
    QString subnode;
    QString subfile;

    libraryPath = libpath;
    clearDB();

    this->findSpinTags(file, "root");

    spinFiles.append(file.mid(file.lastIndexOf("/")+1));
    QStringList keys = db.keys();

#if defined(SPIN_AUTOCOMPLETE)
    makeTags(file);
#endif

    QStringList nodes;
    foreach(key, keys) {
        value = db[key];
        QStringList levels = key.split("/");
        int lcount = levels.count()-1;
        QStringList keyel = levels[lcount].split(KEY_ELEMENT_SEP);
        if(keyel.count() < 2)
            continue;
        if(QString(keyel[0]).compare(keyel[1]) == 0) {
            //qDebug() << key;
            objectInfo(value, subnode, subfile);
            //for(int n = 0; n < lcount; n++) subfile = "    " + subfile;
            nodes.append(subfile);
        }
    }
    // Indent later. Just sort for now.
    nodes.sort(Qt::CaseInsensitive);
    for(int n = 0; n < nodes.count(); n++) {
        QString ns = nodes.at(n);
        spinFiles.append(ns.mid(ns.lastIndexOf(":")+1));
    }

    return spinFiles;
}

void SpinParser::makeTags(QString file)
{
    QStringList keys = db.keys();

    QString tagheader = \
"!_TAG_FILE_FORMAT	1	/original ctags format/\n"
"!_TAG_FILE_SORTED	1	/0=unsorted, 1=sorted, 2=foldcase/\n"
"!_TAG_PROGRAM_AUTHOR	Darren Hiebert	/dhiebert@users.sourceforge.net/\n"
"!_TAG_PROGRAM_NAME	Exuberant Ctags	//\n"
"!_TAG_PROGRAM_URL	http://ctags.sourceforge.net	/official site/\n"
"!_TAG_PROGRAM_VERSION	5.8	//\n";

    QString path = file.mid(0,file.lastIndexOf("/")+1);
    QFile tags(path+"tags");
    if(tags.open(QFile::WriteOnly | QFile::Text)) {
        tags.write(tagheader.toLatin1());
        foreach(QString key, keys) {
            QStringList tl = db[key].split("\t");
            QString ts = tl[0]+"\t"+tl[1]+"\t/^"+tl[2]+"$/";
            tags.write(ts.toLatin1()+"\n");
        }
        tags.close();
    }

}

QString SpinParser::tagItem(QStringList tabs, int field)
{
    QString s;
    if(tabs.length() > 2)
        s = tabs.at(3)+"\t"+tabs.at(field);
    return s;
}

/*
 * all symbols are accessible by key.
 * a key is a name list such as "/root/obj/subobj/subsubobj"
 * if the key is empty, return the root or top file list
 */
QStringList SpinParser::spinSymbols(QString file, QString objname)
{
    QStringList list;
    QStringList keys = db.keys();
    QString key;
    QString value;
    for(int n = 0; n < keys.length(); n++) {
        key = keys[n];
        value = db[key];
        if(objname.length() > 0) {
            if(key.contains(objname+":",Qt::CaseInsensitive)) {
                QStringList tabs = value.split("\t");
                if(tabs.count() > 2)
                    list.append(tagItem(tabs,2));
            }
        }
        else if(value.contains(file,Qt::CaseInsensitive)) {
            QStringList tabs = value.split("\t");
            if(tabs.count() > 2)
                list.append(tagItem(tabs,2));
        }
    }
    QStringList plist;
    QString     order("ofpcevx");

    // sort the list by the order we want to have
    // list.sort(); // could presort make it easier?
    for(int j = 0; j < order.length(); j++) {
        for(int n = 0; n < list.length(); n++) {
            if(list.at(n).at(0) == order[j]) {
                plist.append(list.at(n));
            }
        }
    }
    return plist;
}

/*
 * these are convenience wrappers for typefilter
 */
QStringList SpinParser::spinConstants(QString file, QString objname)
{
    QStringList list;
    QStringList keys = db.keys();
    QString key;
    QString value;
    for(int n = 0; n < keys.length(); n++) {
        key = keys[n];
        value = db[key];
        if(objname.length() > 0) {
            if(key.contains(objname+":",Qt::CaseInsensitive)) {
                QStringList tabs = value.split("\t");
                if(tabs.count() > 2) {
                    if(QString(tabs.at(3)).contains("c",Qt::CaseInsensitive))
                        list.append(tagItem(tabs,2));
                    else if(QString(tabs.at(3)).contains("e",Qt::CaseInsensitive)) {
                        list.append(tagItem(tabs,0));
                    }
                }
            }
        }
        else if(value.contains(file,Qt::CaseInsensitive)) {
            QStringList tabs = value.split("\t");
            if(tabs.count() > 2) {
                if(QString(tabs.at(3)).contains("c",Qt::CaseInsensitive))
                    list.append(tagItem(tabs,2));
                else if(QString(tabs.at(3)).contains("e",Qt::CaseInsensitive)) {
                    list.append(tagItem(tabs,0));
                }
            }
        }
    }
    return list;
}

QStringList SpinParser::spinMethods(QString file, QString objname)
{
    QStringList list;
    QStringList keys = db.keys();
    QString key;
    QString value;
    for(int n = 0; n < keys.length(); n++) {
        key = keys[n];
        value = db[key];
        if(objname.length() > 0) {
            if(key.contains(objname+":",Qt::CaseInsensitive)) {
                QStringList tabs = value.split("\t");
                if(tabs.count() > 2) {
                    if(QString(tabs.at(3)).contains("p",Qt::CaseInsensitive))
                        list.append(tagItem(tabs,2)+"\t"+tagItem(tabs,4));
                    if(QString(tabs.at(3)).contains("f",Qt::CaseInsensitive))
                        list.append(tagItem(tabs,2)+"\t"+tagItem(tabs,4));
                }
            }
        }
        else if(value.contains(file,Qt::CaseInsensitive)) {
            QStringList tabs = value.split("\t");
            if(tabs.count() > 2) {
                if(QString(tabs.at(3)).contains("p",Qt::CaseInsensitive))
                    list.append(tagItem(tabs,2)+"\t"+tagItem(tabs,4));
                if(QString(tabs.at(3)).contains("f",Qt::CaseInsensitive))
                    list.append(tagItem(tabs,2)+"\t"+tagItem(tabs,4));
                if(QString(tabs.at(3)).contains("o",Qt::CaseInsensitive))
                    list.append(tagItem(tabs,2)+"\t"+tagItem(tabs,4));
            }
        }
    }
    return list;
}

QStringList SpinParser::spinDat(QString file, QString objname)
{
    QStringList list;
    QStringList keys = db.keys();
    QString key;
    QString value;
    for(int n = 0; n < keys.length(); n++) {
        key = keys[n];
        value = db[key];
        if(key.contains(objname+":",Qt::CaseInsensitive)) {
            QStringList tabs = value.split("\t");
            if(tabs.count() > 2) {
                if(QString(tabs.at(3)).contains("d",Qt::CaseInsensitive))
                    list.append(tagItem(tabs,2));
            }
        }
    }
    return list;
}

QStringList SpinParser::spinVars(QString file, QString objname)
{
    QStringList list;
    QStringList keys = db.keys();
    QString key;
    QString value;
    for(int n = 0; n < keys.length(); n++) {
        key = keys[n];
        value = db[key];
        if(key.contains(objname+":",Qt::CaseInsensitive)) {
            QStringList tabs = value.split("\t");
            if(tabs.count() > 2) {
                if(QString(tabs.at(3)).contains("v",Qt::CaseInsensitive))
                    list.append(tagItem(tabs,2));
            }
        }
    }
    return list;
}

QStringList SpinParser::spinObjects(QString file, QString objname)
{
    QStringList list;
    QStringList keys = db.keys();
    QString key;
    QString value;
    for(int n = 0; n < keys.length(); n++) {
        key = keys[n];
        value = db[key];
        if(key.contains(objname+":",Qt::CaseInsensitive)) {
            QStringList tabs = value.split("\t");
            if(tabs.count() > 2) {
                if(QString(tabs.at(3)).contains("o",Qt::CaseInsensitive))
                    list.append(tagItem(tabs,2));
            }
        }
    }
    return list;
}

/*
 *   FUNCTION DEFINITIONS
 */

/* Match the name of a tag (function, variable, type, ...) starting at pos. */
char const *SpinParser::extract_name (char const *pos, QString &name)
{
    while (isspace (*pos))
        pos++;
    name.clear();
    for (; *pos && !isspace (*pos) && *pos != '(' && *pos != ','; pos++)
        name.append(*pos);
    return pos;
}

int SpinParser::tokentype(QString tmp)
{
    int rc = K_NONE;
    int i;
    /* find out token type for parser states */
    if(tmp.indexOf(' ') > 0) {
        tmp = tmp.mid(0,tmp.indexOf(' '));
    }
    for (i = 0; spin_keywords[i].token != NULL; i++)
    {
        if (tmp.compare(spin_keywords[i].token,Qt::CaseInsensitive) == 0) {
            rc = spin_keywords[i].kind;
            break;
        }
    }
    return rc;
}

/* Match a keyword starting at p (case insensitive). */
int SpinParser::match_keyword (const char *p, KeyWord const *kw, QString &tag)
{
    QString name;
    int i;
    int j;
    int rc = K_NONE;
    char *tmp = (char*)p;

    /* printf ("match_keyword\n"); */

    for (i = 0; i < kw->token.length(); i++)
    {
        if (tolower (p[i]) != kw->token[i])
            return rc;
    }

    p += i;
    if(*p && !isspace(*p))
        return K_NONE;

    /* if p stands alone on the line, don't add it */
    if(strlen(p) == 0)
         return tokentype(tmp);

    for (j = 0; j < 1 + kw->skip; j++)
    {
        p = extract_name (p, name);
    }
    /* printf ("match_keyword name %d %s\n", name->length, name->buffer); */

    if(name.length() > 0) {
        rc = tokentype(kw->token);
        tag = name+"\t"+currentFile+"\t"+tmp+"\t"+SpinKinds[rc].letter;
    }
    return rc;
}

void SpinParser::match_constant (QString p, int line)
{
    QString tag("");
    int len;
    bool ok;
    p = p.trimmed();
    // ENUM is a constant like #n, NAME, NAME2
    if((p.indexOf("#")) == 0) {
        // add to database
        QString s = p.mid(1).trimmed();
        QStringList list = s.split(",");
        for(int n = 0; n < list.count(); n++) {
            s = QString(list[n]).trimmed();
            s.toInt(&ok);  // don't add numbers to the list
            if(ok == true) continue;
            tag = s+"\t"+currentFile+"\t"+p+"\t"+"e";
            QString key = objectNode+KEY_ELEMENT_SEP+s;
            if(db.contains(key) == false)
                db.insert(key,tag+"\t"+QString("%1").arg(line));
            // qDebug() << objectNode << " ## " << tag;
        }
    }
    else if((len = p.indexOf("=")) > 0) {
        if(p.contains(QRegExp("\\b?=\\b")))
            len = 0;
        if(len > 0) {
            // add to database
            QString s = p.mid(0,len).trimmed();
            if(s.indexOf("con",0,Qt::CaseInsensitive) == 0)
                s = s.mid(4);
            s = s.trimmed();
            tag = s+"\t"+currentFile+"\t"+p+"\t"+SpinKinds[K_CONST].letter;
            QString key = objectNode+KEY_ELEMENT_SEP+s;
            if(db.contains(key) == false)
                db.insert(key,tag+"\t"+QString("%1").arg(line));
            // qDebug() << objectNode << " ## " << tag;
        }
    }
}

void SpinParser::match_dat (QString p, int line)
{
    QString tag = "";
    QString s = p.trimmed();
    if(s.indexOf("dat",0,Qt::CaseInsensitive) == 0)
        s = s.mid(4);
    s = s.trimmed();
    QRegExp regex("\\b(byte|long|word)\\b");
    regex.setCaseSensitivity(Qt::CaseInsensitive);
    if(s.contains(regex)) {
        s = s.mid(0,s.indexOf(regex));
        s = s.trimmed();
        QStringList list = s.split(",",QString::SkipEmptyParts);
        if(list.length() > 0) {
            foreach(s,list) {
                if(s.contains("["))
                    s = s.mid(0,s.indexOf("["));
                s = s.trimmed();
                tag = s+"\t"+currentFile+"\t"+p+"\t"+SpinKinds[K_DAT].letter;
                QString key = objectNode+KEY_ELEMENT_SEP+s;
                if(db.contains(key) == false)
                    db.insert(key,tag+"\t"+QString("%1").arg(line));
                // qDebug() << objectNode << " DAT " << tag;
            }
        }
    }
}

void SpinParser::match_object (QString p, int line)
{
    QString tag = "";
    QString subfile = "";
    QString subnode = "";
    int len = p.indexOf(":");
    if(p.indexOf(":=") > 0)
        len = 0;
    if(len > 0) {
        // add to database
        QString s = p.mid(0,len);
        if(s.contains("OBJ",Qt::CaseInsensitive))
            s = s.mid(s.indexOf("OBJ",0,Qt::CaseInsensitive)+3);
        if(s.contains("[",Qt::CaseInsensitive))
            s = s.mid(0,s.indexOf("[",0,Qt::CaseInsensitive));
        s = s.trimmed();
        tag = s+"\t"+currentFile+"\t"+p+"\t"+SpinKinds[K_OBJECT].letter;
        objectInfo(tag, subnode, subfile);
        QString file = checkFile(subfile);
        if(QFile::exists(file) == false)
            return;
        QString key = objectNode+"/"+subnode+KEY_ELEMENT_SEP+s;
        if(db.contains(key) == false)
            db.insert(key,tag+"\t"+QString("%1").arg(line));
        // qDebug() << objectNode+"/"+subnode << " :: " << tag;
        findSpinTags(subfile,objectNode+"/"+subnode);
    }
}

void SpinParser::match_pri (QString p, int line)
{
    QString tag("");
    int len = p.indexOf("pri",0,Qt::CaseInsensitive);
    if(len == 0) {
        // add to database
        QString s = p.mid(4);
        if(s.indexOf("|") >= 0)
            s = s.mid(0,s.indexOf("|"));
        if(s.indexOf(":") >= 0)
            s = s.mid(0,s.indexOf(":"));
        if(s.indexOf("(") >= 0)
            s = s.mid(0,s.indexOf("("));
        s = s.trimmed();
        tag = s+"\t"+currentFile+"\t"+p+"\t"+SpinKinds[K_PRI].letter;
        QString key = objectNode+KEY_ELEMENT_SEP+s;
        if(db.contains(key) == false)
            db.insert(key,tag+"\t"+QString("%1").arg(line));
        // qDebug() << objectNode << " pri " << tag;
    }
}

void SpinParser::match_pub (QString p, int line)
{
    QString tag("");
    int len = p.indexOf("pub",0,Qt::CaseInsensitive);
    if(len == 0) {
        // add to database
        QString s = p.mid(4);
        if(s.indexOf("|") >= 0)
            s = s.mid(0,s.indexOf("|"));
        if(s.indexOf(":") >= 0)
            s = s.mid(0,s.indexOf(":"));
        if(s.indexOf("(") >= 0)
            s = s.mid(0,s.indexOf("("));
        s = s.trimmed();
        tag = s+"\t"+currentFile+"\t"+p+"\t"+SpinKinds[K_PUB].letter;
        QString key = objectNode+KEY_ELEMENT_SEP+s;
        if(db.contains(key) == false)
            db.insert(key,tag+"\t"+QString("%1").arg(line));
        // qDebug() << objectNode << " pub " << tag;
    }
}

void SpinParser::match_var (QString p, int line)
{
    QString tag = "";
    QString s = p.trimmed();
    if(s.indexOf("var",0,Qt::CaseInsensitive) == 0)
        s = s.mid(4);
    s = s.trimmed();
    QRegExp regex("\\b(byte|long|word)\\b");
    regex.setCaseSensitivity(Qt::CaseInsensitive);
    if(s.contains(regex)) {
        QString s = p.trimmed();
        if(s.indexOf("var",0,Qt::CaseInsensitive) == 0)
            s = s.mid(4);
        s = s.trimmed();
        if(s.indexOf(regex) == 0) {
            s = s.mid(s.indexOf(regex)+4);
            QStringList list = s.split(",",QString::SkipEmptyParts);
            if(list.length() > 0) {
                foreach(s,list) {
                    if(s.contains("["))
                        s = s.mid(0,s.indexOf("["));
                    s = s.trimmed();
                    tag = s+"\t"+currentFile+"\t"+p+"\t"+SpinKinds[K_VAR].letter;
                    QString key = objectNode+KEY_ELEMENT_SEP+s;
                    if(db.contains(key) == false)
                        db.insert(key,tag+"\t"+QString("%1").arg(line));
                    // qDebug() << objectNode << " VAR " << tag;
                }
            }
        }
    }
}

int SpinParser::objectInfo(QString tag, QString &name, QString &file)
{
    QStringList list = tag.split("\t",QString::KeepEmptyParts);
    if(list.length() == 0)
        return 0;
    name = QString(list[0]).trimmed();
    file = QString(list[2]).trimmed();
    file = file.mid(file.indexOf("\"")+1);
    file = file.mid(0,file.indexOf("\""));
    file = file.trimmed();

    if(file.contains(".spin",Qt::CaseInsensitive) == false)
        file += ".spin";

    return list.length();
}


QString SpinParser::checkFile(QString fileName)
{
    QString retfile = fileName;
    QString fs = this->currentFile;
    QString shortfile = fileName.mid(fileName.lastIndexOf("/")+1);
    QString path = fs.mid(0,fs.lastIndexOf("/")+1);

    if(QFile::exists(fileName)) {
        return fileName;
    }
    else if(QFile::exists(path+fileName)) {
        return path+fileName;
    }
    else if(QFile::exists(libraryPath+fileName)) {
        return libraryPath+fileName;
    }
    else {
        QDir dir;
        QStringList list;
        dir.setPath(path);
        list = dir.entryList();
        foreach(QString s, list) {
            if(s.compare(shortfile,Qt::CaseInsensitive) == 0) {
                return path+s;
            }
        }
        dir.setPath(libraryPath);
        list = dir.entryList();
        foreach(QString s, list) {
            if(s.contains(shortfile,Qt::CaseInsensitive)) {
                return libraryPath+"/"+s;
            }
        }
    }
    return retfile;
}

void SpinParser::findSpinTags (QString fileName, QString objnode)
{
    fileName = checkFile(fileName);
    if(QFile::exists(fileName) == false)
        return;

    QString line;
    QString old;
    QString tag;
    QString filestr;

    SpinKind state = K_CONST; // spin starts with CONST
    int blockComment = 0;

    QFile file(fileName);
    QTextStream in(&file);
    in.setAutoDetectUnicode(true);
    if(file.open(QFile::ReadOnly) != true)
        return;
    QStringList list;

    filestr = in.readAll();
    file.close();


    QRegExp spline("\r\n|\n\r|\r|\n");
    spline.setCaseSensitivity(Qt::CaseInsensitive);
    list = filestr.split(spline,QString::KeepEmptyParts);

    QRegExp braces("[{}]");

    QRegExp sregx(
        "\\b"
        "con[ \t\r\n]|"
        "dat[ \t\r\n]|"
        "obj[ \t\r\n]|"
        "pri[ \t\r\n]|"
        "pub[ \t\r\n]|"
        "var[ \t\r\n]"
        "\\b");

    sregx.setCaseSensitivity(Qt::CaseInsensitive);

    for(int n = 0; n < list.length(); n++)
    {
        // give app a chance to do work? parsing can take a while.
        // QApplication::processEvents();

        // do here in case OBJ detect causes context change
        objectNode  = objnode;
        currentFile = fileName;

        if(n) old = list[n-1];
        line = QString(list[n]).trimmed();

        if(line.contains(braces)) {
            QString s = line;
            line = "";
            for(int j = 0; j < s.length(); j++) {
                QChar c = s[j];
                if(c == '{') {
                    blockComment++;
                }
                else if(c == '}') {
                    if(blockComment > 0) // should never go negative
                        blockComment--;
                    else
                        qDebug() << "Negative block comment ?";
                    if(s.trimmed().startsWith("}") && old.trimmed().endsWith("{")) {
                        line = old.replace("{","")+s.mid(1);
                    }
                }
                if(!blockComment) {
                    if(j < s.length()) line += s[j];
                }
            }
        }

        if(blockComment > 0)
            continue;

        if(line.contains("\'")) {
            line = line.mid(0,line.indexOf("\'"));
        }

        /* Empty line? */
        if (line.length() < 1)
            continue;

        line = line.trimmed();

        if(line.indexOf(sregx) == 0) {
            //qDebug() << shortFileName(fileName) << objnode << "Section " << ": " << line;
        }

#if !defined(SPIN_AUTOCOMPLETE)
        /* In Spin, keywords always are at the start of the line. */
        SpinKind type = K_NONE;
        KeyWord kw = spin_keywords[1]; // K_OBJECT is not 1, but OBJ keyword is.
        line = line.trimmed();
        QRegExp tokens("\\b(con|obj|pri|pub|dat|var)\\b");
        tokens.setCaseSensitivity(Qt::CaseInsensitive);
        if(line.indexOf(tokens) == 0) {
            // if line has obj at position 0, get the type and store it
            // else set to some other type so we stop looking for objects
            if(line.indexOf("obj",0,Qt::CaseInsensitive) == 0)
                type = (SpinKind) match_keyword (line.toLatin1(), &kw, tag);
            else
                type = this->K_CONST;

            // keep state until it changes from K_NONE
            if(type != K_NONE) {
                state = type;
            }
        }

        //printf ("state %d\n", state);
        switch(state) {
            case K_OBJECT:
                match_object(line);
            break;
            default:
                tag = "";
            break;
        }
#else
        /* In Spin, keywords always are at the start of the line. */
        SpinKind type = K_NONE;
        foreach(KeyWord kw, spin_keywords) {
            if(kw.kind == K_NONE)
                continue;
            type = K_NONE;
            if(line.indexOf(kw.token,0,Qt::CaseInsensitive) == 0) {
                // if line has a token, get the type and store it
                type = (SpinKind) match_keyword (line.toLatin1(), &kw, tag);

                // keep state until it changes from K_NONE
                if(type != K_NONE) {
                    state = type;
                    break;
                }
            }
        }

        //printf ("state %d\n", state);
        switch(state) {
            case K_CONST:
                match_constant(line, n);
            break;
            case K_DAT:
                match_dat(line, n);
            break;
            case K_OBJECT:
                match_object(line, n);
            break;
            case K_PRI:
                match_pri(line, n);
            break;
            case K_PUB:
                match_pub(line, n);
            break;
            case K_VAR:
                match_var(line, n);
            break;
            default:
                tag = "";
            break;
        }
#endif
    }
}

QString SpinParser::shortFileName(QString fileName)
{
    QString rets;
    if(fileName.indexOf("/") > -1)
        rets = fileName.mid(fileName.lastIndexOf("/")+1);
    else
        rets = fileName;
    return rets;
}

